from fastapi import FastAPI, Query, Path, Request
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field  # 导入 Field
from starlette.middleware.base import BaseHTTPMiddleware
from typing import Literal, Optional
import pandas as pd
import uvicorn
import json
import os

# 导入akshare-one的功能
from akshare_one import (
    get_basic_info,
    get_hist_data,
    get_fund_etf_daily,
    get_fund_overview,
    get_index_stock_info,
    get_index_stock_cons_weight,
    # get_fund_etf_hist_min
)
# 导入缓存模块
from akshare_one.cache import cache_decorator

# 创建FastAPI应用
app = FastAPI(
    title="Akshare One API",
    description="基于 akshare-one 的 FastAPI 服务，提供中国市场金融数据的统一接口",
    version="1.0.0"
)

# 配置CORS
# 从环境变量读取 origins 配置
allowed_origins = os.getenv("ALLOWED_ORIGINS")

# 实现动态CORS中间件
class DynamicCORSMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # 从请求头获取Origin
        origin = request.headers.get("origin")
        
        # 如果存在Origin头，则设置对应的CORS响应头
        if origin:
            # 获取响应
            response = await call_next(request)
            
            # 设置CORS响应头
            response.headers["Access-Control-Allow-Origin"] = origin
            response.headers["Access-Control-Allow-Credentials"] = "true"
            response.headers["Access-Control-Allow-Methods"] = "*"
            response.headers["Access-Control-Allow-Headers"] = "*"
            
            return response
        else:
            # 如果没有Origin头，直接传递请求
            return await call_next(request)

# 根据allowed_origins的值配置不同的CORS策略
if allowed_origins == "dynamic":
    # 使用动态CORS中间件
    app.add_middleware(DynamicCORSMiddleware)
elif allowed_origins:
    # 解析环境变量中的 origins 列表（逗号分隔）
    origins = [origin.strip() for origin in allowed_origins.split(",")]
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

# 辅助函数：将DataFrame转换为可序列化的格式
def df_to_json(df: pd.DataFrame):
    return json.loads(df.to_json(orient="records", force_ascii=False))  # pyright: ignore[reportArgumentType]

class RootResponseModel(BaseModel):
    message: str
    docs: str
    redoc: str

@app.get("/", 
         tags=["根路径"], 
         summary="API根路径", 
         description="提供API的基本信息和文档访问链接", 
         response_description="返回API基本信息和文档链接",
         response_model=RootResponseModel
)
async def root():
    """
    API根路径接口
    
    返回内容:
    - message: 欢迎信息
    - docs: Swagger UI文档地址
    - redoc: ReDoc文档地址
    """
    return {
        "message": "欢迎使用 Akshare One API",
        "docs": "/docs",
        "redoc": "/redoc"
    }

class BasicInfoResponseModel(BaseModel):
    price: float = Field(description="最新价", default=16.63)
    symbol: str = Field(description="股票代码", default="300170")
    name: str = Field(description="股票简称", default="汉得信息")
    total_shares: float = Field(description="总股本", default=1020196311)
    float_shares: float = Field(description="流通股", default=975076509)
    total_market_cap: float = Field(description="总市值", default=16965864651.93)
    float_market_cap: float = Field(description="流通市值", default=16215522344.67)
    industry: str = Field(description="行业", default="互联网服务")
    listing_date: float = Field(description="上市时间（时间戳）", default=1296518400000)

@app.get("/basic-info/{symbol}", tags=["基础信息"], summary="获取股票基础信息", description="根据股票代码获取股票的基本信息，包括价格、市值、流通股等详细数据", response_description="成功返回股票基本信息列表，失败返回错误信息", response_model=list[BasicInfoResponseModel])
@cache_decorator
async def basic_info(
    symbol: str = Path(..., description="股票代码，必填，例如 '600000'", examples=["600000"]),
    source: Literal["eastmoney"] = Query("eastmoney", description="数据源，可选值：'eastmoney'，默认为'eastmoney'")
):
    """
    获取股票基础信息
    
    - **symbol**: 股票代码，例如 '600000'
    - **source**: 数据源，目前支持 'eastmoney'
    
    返回内容:
    - price: 最新价
    - symbol: 股票代码
    - name: 股票简称
    - total_shares: 总股本
    - float_shares: 流通股
    - total_market_cap: 总市值
    - float_market_cap: 流通市值
    - industry: 行业
    - listing_date: 上市时间
    
    错误响应:
    - 400: 请求参数错误
    - 500: 服务器内部错误
    """
    try:
        result = get_basic_info(symbol=symbol, source=source)
        return df_to_json(result)
    except Exception as e:
        return {"error": str(e)}

class HistoricalDataResponseModel(BaseModel):
    timestamp: int = Field(description="时间戳", default=1640995200000)
    open: float = Field(description="开盘价", default=16.63)
    high: float = Field(description="最高价", default=16.63)
    low: float = Field(description="最低价", default=16.5)
    close: float = Field(description="收盘价", default=16.5)
    volume: float = Field(description="成交量", default=123456)

@app.get("/historical-data/{symbol}", tags=["历史数据"], summary="获取股票或基金历史数据", description="根据股票或基金代码获取指定时间范围和频率的历史行情数据", response_description="成功返回历史数据列表，失败返回错误信息", response_model=list[HistoricalDataResponseModel])
@cache_decorator
async def historical_data(
    symbol: str = Path(..., description="股票或基金代码,必填，例如 '600000'", examples=["600000"]),
    interval: Literal["day", "hour", "day", "week", "month", "year"] = Query("day", description="时间间隔"),
    interval_multiplier: int = Query(1, description="时间间隔倍数，例如 5 表示 5分钟，默认为1"),
    start_date: str = Query("2024-01-01", description="开始日期，格式为 YYYY-MM-DD，默认为'2024-01-01'", examples=["2024-01-01"]),
    end_date: str = Query("2024-01-28", description="结束日期，格式为 YYYY-MM-DD，默认为'2024-02-28'", examples=["2024-02-28"]),
    adjust: Literal["none", "qfq", "hfq"] = Query("none", description="复权类型，可选值：'none'(不复权), 'qfq'(前复权), 'hfq'(后复权)，默认为'none'"),
    source: Literal["eastmoney", "eastmoney_direct", "sina"] = Query("eastmoney_direct", description="数据源，可选值：'eastmoney', 'eastmoney_direct', 'sina'，默认为'eastmoney_direct'")
):
    """
    获取股票或基金的历史行情数据
    
    - **symbol**: 股票或基金代码，例如 '600000'
    - **interval**: 时间间隔，可选值：'minute', 'hour', 'day', 'week', 'month', 'year'
    - **interval_multiplier**: 时间间隔倍数，例如 5 表示 5分钟
    - **start_date**: 开始日期，格式为 YYYY-MM-DD
    - **end_date**: 结束日期，格式为 YYYY-MM-DD
    - **adjust**: 复权类型，可选值：'none'(不复权), 'qfq'(前复权), 'hfq'(后复权)
    - **source**: 数据源，可选值：'eastmoney', 'eastmoney_direct', 'sina'
    
    返回内容:
    - timestamp: 时间戳
    - open: 开盘价
    - high: 最高价
    - low: 最低价
    - close: 收盘价
    - volume: 成交量
    
    错误响应:
    - 400: 请求参数错误
    - 500: 服务器内部错误
    """
    try:
        result = get_hist_data(
            symbol=symbol,
            interval=interval,
            interval_multiplier=interval_multiplier,
            start_date=start_date,
            end_date=end_date,
            adjust=adjust,
            source=source
        )
        return df_to_json(result)
    except Exception as e:
        return {"error": str(e)}


class FundEtfDailyResponseModel(BaseModel):
    基金代码: str = Field(examples=["511010"], description="基金代码")
    基金简称: str = Field(examples=["国泰上证5年期国债E"], description="基金简称")
    类型: str = Field(examples=["指数型-固收"], description="基金类型")
    增长值: str = Field(examples=["-0.0260"], description="增长值")
    增长率: str = Field(examples=["-0.02%"], description="增长率")
    市价: str = Field(examples=["140.6140"], description="市价")
    折价率: str = Field(examples=["0.00%"], description="折价率")

@app.get("/fund/etf-list", tags=["基础信息"], summary="获取场内交易基金列表", description="获取所有场内交易基金的基本信息列表，包括净值、涨跌幅等数据", response_description="成功返回基金列表数据，失败返回错误信息", response_model=list[FundEtfDailyResponseModel])
@cache_decorator
async def fund_etf_daily(
    source: Literal["sina"] = Query("sina", description="数据源，可选值：'sina'，默认为'sina'")
):
    """
    获取所有场内交易基金列表数据
    
    - **source**: 数据源，目前支持 'sina'
    
    返回内容:
    - 基金代码
    - 基金简称
    - 类型
    - 增长值
    - 增长率
    - 市价
    - 折价率
    
    错误响应:
    - 500: 服务器内部错误
    """
    try:
        result = get_fund_etf_daily(source=source)
        return df_to_json(result)
    except Exception as e:
        return {"error": str(e)}


class FundOverviewResponseModel(BaseModel):
    基金全称: str = Field(examples=["华夏黄金交易型开放式证券投资基金"], description="基金全称")
    基金简称: str = Field(examples=["华夏黄金ETF"], description="基金简称")
    基金代码: str = Field(examples=["518850（主代码）"], description="基金代码")
    基金类型: str = Field(examples=["指数型-其他"], description="基金类型")
    发行日期: str = Field(examples=["2020年03月27日"], description="发行日期")
    成立日期: str = Field(examples=["2020年04月13日"], description="成立日期")
    成立规模: str = Field(examples=["3.336亿份"], description="成立规模")
    资产规模: str = Field(examples=["58.28亿元（截止至：2025年09月30日）"], description="资产规模")
    份额规模: str = Field(examples=["6.9428亿份（截止至：2025年09月30日）"], description="份额规模")
    基金管理人: str = Field(examples=["华夏基金"], description="基金管理人")
    基金托管人: str = Field(examples=["建设银行"], description="基金托管人")
    基金经理人: str = Field(examples=["荣膺"], description="基金经理人")
    成立来分红: str = Field(examples=["每份累计0.00元（0次）"], description="成立来分红")
    管理费率: str = Field(examples=["0.15%（每年）"], description="管理费率")
    托管费率: str = Field(examples=["0.05%（每年）"], description="托管费率")
    销售服务费率: str = Field(examples=["0.05%（每年）"], description="销售服务费率")
    最高认购费率: str = Field(examples=["0.05%（每手）"], description="最高认购费率")
    最高申购费率: str = Field(examples=["0.05%（每手）"], description="最高申购费率")
    最高赎回费率: str = Field(examples=["0.05%（每手）"], description="最高赎回费率")
    业绩比较基准: str = Field(examples=["上海黄金交易所黄金现货实盘合约Au99.99价格收益率"], description="业绩比较基准")
    跟踪标的: str = Field(examples=["黄金9999"], description="跟踪标的")


@app.get("/fund/basic-info/{symbol}", tags=["基础信息"], summary="获取基金基本信息", description="根据基金代码获取基金的详细基本信息，包括基金经理、费率、规模等数据", response_description="成功返回基金详细信息，失败返回错误信息", response_model=list[FundOverviewResponseModel])
@cache_decorator
async def fund_overview(
    symbol: str = Path(..., description="基金代码，必填，例如 '518850'", examples=["518850"]),
    source: Literal["sina"] = Query("sina", description="数据源，可选值：'sina'，默认为'sina'")
):
    """
    获取基金基本信息概况数据
    
    - **symbol**: 基金代码，例如 '518850'
    - **source**: 数据源，目前支持 'sina'
    """
    try:
        result = get_fund_overview(symbol=symbol, source=source)
        json_result = df_to_json(result)
        if "成立日期/规模" in json_result[0]:
            json_result[0]["成立日期"] = json_result[0]["成立日期/规模"].split(" / ")[0]
            json_result[0]["成立规模"] = json_result[0]["成立日期/规模"].split(" / ")[1]
        return json_result
    except Exception as e:
        return {"error": str(e)}

class IndexStockInfoResponseModel(BaseModel):
    index_code: str = Field(examples=["000001"], description="指数代码")
    display_name: str = Field(examples=["上证指数"], description="指数名称")
    publish_date: str = Field(examples=["1991-07-15"], description="发布日期")

@app.get("/index/stock-info", tags=["指数信息"], summary="获取股票指数信息一览表", description="获取所有股票指数的基本信息，包括指数代码、指数名称和发布日期", response_description="成功返回指数信息列表，失败返回错误信息", response_model=list[IndexStockInfoResponseModel])
@cache_decorator
async def index_stock_info(
    source: Literal["eastmoney"] = Query("eastmoney", description="数据源，可选值：'eastmoney'，默认为'eastmoney'")
):
    """
    获取股票指数信息一览表
    
    - **source**: 数据源，目前支持 'eastmoney'
    
    返回内容:
    - index_code: 指数代码
    - display_name: 指数名称
    - publish_date: 发布日期
    """
    result = get_index_stock_info(source=source)
    return df_to_json(result)

class IndexStockConsWeightResponseModel(BaseModel):
    日期: int = Field(examples=[1761868800000], description="日期")
    指数代码: str = Field(examples=["000300"], description="指数代码")
    指数名称: str = Field(examples=["沪深300"], description="指数名称")
    指数英文名称: str = Field(examples=["CSI 300"], description="指数英文名称")
    成分券代码: str = Field(examples=["000001"], description="成分券代码")
    成分券名称: str = Field(examples=["平安银行"], description="成分券名称")
    成分券英文名称: str = Field(examples=["Ping An Bank Co., Ltd."], description="成分券英文名称")
    交易所: str = Field(examples=["深圳证券交易所"], description="交易所")
    交易所英文名称: str = Field(examples=["Shenzhen Stock Exchange"], description="交易所英文名称")
    权重: float = Field(examples=[0.445], description="权重")

@app.get("/index/cons-weight/{symbol}", tags=["指数信息"], summary="获取中证指数成份股权重", description="根据指数代码获取该指数的成份股信息和权重数据", response_description="成功返回成份股权重列表，失败返回错误信息", response_model=list[IndexStockConsWeightResponseModel])
@cache_decorator
async def index_stock_cons_weight(
    symbol: str = Path(..., description="指数代码，必填，例如 '000300'", examples=["000300"]),
    source: Literal["eastmoney"] = Query("eastmoney", description="数据源，可选值：'eastmoney'，默认为'eastmoney'")
):
    """
    获取中证指数成份股权重
    
    - **symbol**: 指数代码，例如 '000300' (沪深300)
    - **source**: 数据源，目前支持 'eastmoney'
    
    返回内容:
    - 日期: 数据日期
    - 指数代码: 指数代码
    - 指数名称: 指数名称
    - 指数英文名称: 指数英文名称
    - 成分券代码: 成分股代码
    - 成分券名称: 成分股名称
    - 成分券英文名称: 成分股英文名称
    - 交易所: 交易所名称
    - 交易所英文名称: 交易所英文名称
    - 权重: 成分股权重
    """
    result = get_index_stock_cons_weight(symbol=symbol, source=source)
    return df_to_json(result)


# class FundEtfHistMinResponseModel(BaseModel):
#     时间: str = Field(examples=["2025-11-20 09:30:00"], description="交易时间")
#     开盘价: float = Field(examples=[123.45], description="开盘价")
#     收盘价: float = Field(examples=[123.50], description="收盘价")
#     最高价: float = Field(examples=[123.55], description="最高价")
#     最低价: float = Field(examples=[123.40], description="最低价")
#     成交量: int = Field(examples=[10000], description="成交量")
#     成交额: float = Field(examples=[1234567.89], description="成交额")
#     均价: float = Field(examples=[123.475], description="均价")

# @app.get("/fund/etf-hist-min/{symbol}", tags=["历史数据"], summary="获取基金ETF分钟级别数据", response_model=list[FundEtfHistMinResponseModel])
# @cache_decorator
# async def fund_etf_hist_min(
#     symbol: str = Path(..., description="基金代码，必填，例如 '518850'", example="518850") ,
#     period: str = Query("60", description="时间周期，可选值为{'5', '15', '30', '60'}，默认为'60'", example="1"),
#     adjust: str = Query("qfq", description="复权类型，可选值为{'', 'qfq', 'hfq'}，默认为'qfq'"),
#     start_date: str = Query(None, description="开始时间，格式为 'YYYY-MM-DD HH:MM:SS'", example="2025-11-20 09:30:00") ,
#     end_date: str = Query(None, description="结束时间，格式为 'YYYY-MM-DD HH:MM:SS'", example="2025-11-20 17:40:00") ,
#     source: Literal["sina"] = Query("sina", description="数据源，可选值：'sina'，默认为'sina'")
# ):
#     """
#     获取基金ETF或者股票分钟级别数据
    
#     - symbol: 基金代码，例如 '518850'
#     - period: 时间周期，可选值为{"5", "15", "30", "60"}，默认为"60"
#     - adjust: 复权类型，可选值为{'', 'qfq', 'hfq'}，默认为'qfq'
#     - start_date: 开始时间，例如 '2025-11-20 09:30:00'
#     - end_date: 结束时间，例如 '2025-11-20 17:40:00'
#     - source: 数据源，目前支持 'sina'
    
#     返回内容:
#     - 时间
#     - 开盘价
#     - 收盘价
#     - 最高价
#     - 最低价
#     - 成交量
#     - 成交额
#     - 均价
    
#     """
#     result = get_fund_etf_hist_min(
#         symbol=symbol,
#         period=period,
#         adjust=adjust,
#         start_date=start_date,
#         end_date=end_date,
#         source=source
#     )
#     return df_to_json(result)

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)


